<?php

// +-------------------------------------------
// | Name: 星空后台 中间件处理
// +-------------------------------------------
// | Version: V5.0
// +-------------------------------------------
// | Author: xkyinzhe <zhonghuaxinxing@sina.cn>
// +-------------------------------------------
// | Time: 2021-04-08 10:19
// +-------------------------------------------
declare(strict_types=1);

namespace app\middleware;

use app\common\services\JwtServices;
use think\facade\Cache;
use think\facade\Config;
use think\facade\Session;
use app\common\model\Admin as AdminModel;
use app\common\model\User as UserModel;
use think\Request;
/**
 * @xk 中间件
 */
class Check
{
    use \app\common\traits\Jump;

    /**
     * @xk 请求处理
     *
     * @method handle
     *
     * @param Request $request [description]
     * @param Closure $next    [description]
     *
     * @return [type] [description]
     * @Author xkyinzhe                <zhonghuaxinxing@sina.cn>
     * @Time   2021-04-08T10:21:16+080
     */
    public function handle(Request $request, \Closure $next)
    {
        // 开始判断 前端数据来源
        $currentUrl = $request->url();
        $baseUrl = $request->baseUrl();
        $controller = $request->controller();
        $version = config('manage.version').'.';
        $controller = str_replace($version, '', $controller);
        $action = $request->action();
        $now_url = $controller.'/'.$action;
        if (0 === stripos($currentUrl, '/jpyepmc/v1') || 0 === stripos($currentUrl, '/jpyepmc')) {
            self::xingkong($currentUrl, $baseUrl, $request, $now_url); //跳转到后台站点管理
        } elseif (0 === stripos($currentUrl, '/index/')) {
            return $next($response);
        } elseif (0 === stripos($currentUrl, '/api/v1') || 0 === stripos($currentUrl, '/api/v2')) {
            $newToken = self::checkAuth($request, $currentUrl,$baseUrl);
            header('token:'.$newToken);
        } else {
            Session::clear();
            self::xingkong($currentUrl, $baseUrl, $request, $now_url); //跳转到后台站点管理
        }

        return $next($request);
    }

    /**
     * @xk 后台处理 增加token处理
     *
     * @method xingkong
     *
     * @param [type]  $currentUrl [description]
     * @param [type]  $baseUrl    [description]
     * @param Request $request    [description]
     * @param [type]  $now_url    [description]
     *
     * @return [type] [description]
     * @Author xkyinzhe                <zhonghuaxinxing@sina.cn>
     * @Time   2021-04-08T10:23:07+080
     */
    public function xingkong($currentUrl, $baseUrl, Request $request, $now_url)
    {
        $adminModel = new AdminModel();
        // 获取 link.php 里面的配置 由于此时 TP 并没有初始化 自定义的配置,所以只能通过 require 的方式 获取 配置
        // 拼接出 登录的后缀
        $_loginUri = '/'.config('manage.manage_name').'/'.config('manage.version').'/login';
        $_allowedUri = [
              strtolower($_loginUri),        //  根路径 也是登录路径
              strtolower($_loginUri.'/checkLogin'),
          ];
        $allow_url = [
            'index/index',
            'index/getleftmenu',
            'index/welcome',
            'index/getusercount',
            'index/getordercount',
            'index/getgoodscount',
            'index/getfinancecount',
            'login/index',
            'login/checklogin',
            'login/loginout',
        ];
        if (!in_array(strtolower($baseUrl), $_allowedUri)) {
            if (!Session::has('admin_id')) {
                $this->error('访问无效,请重新登录', $_loginUri);
            } else {
                $admin_id = Session::get('admin_id');
                $user_info = $adminModel->withTrashed()->field('`login_ip`,`login_time`,`auth_token`')->find($admin_id);
                // 验证对应的设备终端ID是否和当前一致
                $now_ip = getRealIP();
                $login_num = Session::get('admin_num'); // 登录账号
                // $auth_key = Session::get('auth_key');
                $auth_key = makePass($login_num.$now_ip,'20240304');
                if(!$user_info || $auth_key != $user_info['auth_token']){
                  if($request->isAjax()){
                    $this->result(200,'请求成功',404,'当前账号已退出，请重新登录');
                  }else{
                    Session::clear();
                    $this->error('当前账号已退出，请重新登录', $_loginUri);
                  }
                }
                // 验证对应的权限是否有效 如果有效则不操作 无效则开始执行进一步操作
                $auth_rule_links = [];
                if (Session::has('auth_rule_links')) {
                    $auth_rule_links = Session::get('auth_rule_links');
                }
                if (!in_array(strtolower($now_url), $auth_rule_links) && !in_array(strtolower($now_url), $allow_url) && Session::get('admin_num') != config('manage.suppler_admin_num')) {
                    $jump_url = '/'.config('manage.manage_name').'/'.config('manage.version').'/Index/welcome';
                    if($request->isAjax()){
                      $this->result(200,'请求成功',404,'权限不足，请联系超级管理员处理');
                    }else{
                      $this->error('权限不足，请联系管理员开放');
                    }
                }
            }
        }
    }

    /**
     * @xk 接口鉴权
     *
     * @method checkAuth
     *
     * @param [type] $request    [description]
     * @param [type] $currentUrl [description]
     *
     * @return [type] [description]
     * @Author xkyinzhe                <zhonghuaxinxing@sina.cn>
     * @Time   2021-05-07T14:27:38+080
     */
    public function checkAuth(Request $request, $currentUrl,$baseUrl)
    {
        $userModel = new UserModel();
        $header = $request->header();
        $code = 200;
        $msg = '请求成功';
        $newToken = null;
        $content = "=================【".$baseUrl."】==============================\n";
        $content .= json_encode($request->param())."\n";
        $_loginUri = '/api/v1';
        $_allowedUri = [
              strtolower($_loginUri),        //  根路径 也是登录路径
              strtolower($_loginUri.'/getwechatinfo'),
              strtolower($_loginUri.'/getversion'),
              strtolower($_loginUri.'/news/getwebshow'),
              strtolower($_loginUri.'/ajax/updateusercount'),
              strtolower($_loginUri.'/ajax/doCommit'),
              strtolower($_loginUri.'/ajax/Cleanusers'),
          ];
        createLog('zhuabao/',$content);
        // 先一步执行鉴权校验
        if(!in_array(strtolower($baseUrl), $_allowedUri)){
          $param = $request->param();
          $sign = isset($param['sign']) ? $param['sign'] : '';
          $old_sign_str = isset($param['sign_str']) ? $param['sign_str'] : '';
          unset($param['sign']); unset($param['version']);
          unset($param['sign_str']);
          $sign_str = '';
          foreach ($param as $key => $value) {
            if($sign_str == ''){
              $sign_str .= $key.'='.json_encode($value,JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES);
            }else{
              $sign_str .= '&'.$key.'='.json_encode($value,JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES);
            }
          }
          $key = config('core_config.api_config.auth_key');
          $check_sign = md5(md5(encodeURIComponent($sign_str).$key).$key);
        //   var_dump($check_sign);die;
          if ($check_sign != $sign) {
              $this->result(200, '请求成功', 404, '校验失败，请重新提交处理');
              die();
          }
        }

        if (array_key_exists('token', $header) && $header['token']) {
            $jwtServices = new JwtServices($header['token']);
            try {
                $tokenArr = $jwtServices->parseToken();
                $newToken = $header['token'];
                if (-1 == $tokenArr['code']) {
                    // 检测是否在文件token黑名单
                    $list = Cache::store('file')->get('tokenBlackList', []);
                    $str = $tokenArr['data']->jti.'_'.$tokenArr['data']->iat;
                    if (in_array($str, $list)) {
                        if (!$newToken = Cache::store('file')->get('black_token:'.$header['token'])) {
                            throw new \think\Exception('登录已经过期', 205);
                        }
                    } else {
                        // 颁发新token
                        $jwtServices = new JwtServices(['param' => ['user_sn' => $tokenArr['data']->param->user_sn,'be_from' => $tokenArr['data']->param->be_from]]);
                        $newToken = $jwtServices->issueToken();

                        // 加入文件黑名单
                        $jwtServices->addBlackList($tokenArr['data']);
                        // 列入余地黑名单,30秒有效期
                        Cache::store('file')->set('black_token:'.$header['token'], $newToken, 30);
                    }
                }
                $request->user_sn = $tokenArr['data']->param->user_sn;
                $param = $request->param();
                $user_sn = isset($param['user_sn'])?$param['user_sn']:'';
                $user_info = $userModel
                            ->where([['user_sn','=',$user_sn]])
                            ->field('`auth_token`,`status`')
                            ->find();
                $user_info = $user_info?$user_info->toArray():[];
                if($user_info && $user_sn != ''){
                  if($user_info['status'] != 1){
                    throw new \think\Exception("账户已被冻结，请联系管理处理", 205);
                  }
                  if($user_info['auth_token'] != $header['token']){
                    // 如果传递的token 和之前保存的token 不一致 说明终端已经更换了
                    throw new \think\Exception("当前账号已退出，请重新登录", 205);
                  }
                  $userModel->where([['user_sn','=',$user_sn]])->save(['auth_token'=>$newToken]); // 将登录token写入平台
                }
                if($user_sn && $user_sn != $tokenArr['data']->param->user_sn){
                    throw new \think\Exception('请求信息与登录信息不符，请重新登录', 205);
                }
                $param['user_sn'] = $tokenArr['data']->param->user_sn;
                $request->param = $param;
            } catch (\Exception $e) {
                $result_code = 404;
                if($e->getCode() == 205){
                  $result_code = 205;
                }else if($e->getCode() == 300){
                  $resultData['jumpUrl'] = "/pages/login/login";
                  $result_code = 300;
                }
                $this->result($code, $msg, $result_code, $e->getMessage());
            }
        }

        return $newToken;
    }
}
